package com.ssy.lingxi.report.serviceimpl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.order.OrderPayChannelEnum;
import com.ssy.lingxi.common.constant.settle.SettlementMemberOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.message.api.vo.request.SystemMessageRequest;
import com.ssy.lingxi.report.dao.aftersale.ReturnGoodsDAO;
import com.ssy.lingxi.report.dao.enhance.ProduceNoticeOrderDAO;
import com.ssy.lingxi.report.dao.logistics.LogisticsOrderDAO;
import com.ssy.lingxi.report.dao.merchantmember.MemberDAO;
import com.ssy.lingxi.report.dao.order.ProcurementOrderDAO;
import com.ssy.lingxi.report.dao.settleaccount.MemberReceiptInvoiceDAO;
import com.ssy.lingxi.report.entity.settleaccount.*;
import com.ssy.lingxi.report.model.bo.*;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementStatusEnum;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementStrategyStatusEnum;
import com.ssy.lingxi.report.model.constant.settleaccount.SettlementWayEnum;
import com.ssy.lingxi.report.model.vo.convert.MemberOrderSettlementCVO;
import com.ssy.lingxi.report.model.vo.request.MemberManualSettlementVO;
import com.ssy.lingxi.report.repository.settleaccount.*;
import com.ssy.lingxi.report.service.IFeignService;
import com.ssy.lingxi.report.service.IMemberSettlementService;
import com.ssy.lingxi.report.utils.SerialNumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class MemberSettlementServiceImpl implements IMemberSettlementService {

    private static final Logger logger = LoggerFactory.getLogger(MemberSettlementServiceImpl.class);

    @Resource
    private MemberSettlementStrategyRepository memberSettlementStrategyRepository;

    @Resource
    private LogisticsOrderDAO logisticsOrderDAO;

    @Resource
    private MemberSettlementRepository memberSettlementRepository;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private MemberDAO memberDAO;

    @Resource
    private MemberProductionNoticeSettlementRepository memberProductionNoticeSettlementRepository;

    @Resource
    private MemberOrderSettlementRepository memberOrderSettlementRepository;

    @Resource
    private MemberLogisticsSettlementRepository memberLogisticsSettlementRepository;

    @Resource
    private ProduceNoticeOrderDAO produceNoticeOrderDAO;

    @Resource
    private MemberReceiptInvoiceDAO memberReceiptInvoiceDAO;

    @Resource
    private ReturnGoodsDAO returnGoodsDAO;

    @Resource
    private ProcurementOrderDAO procurementOrderDAO;

    @Resource
    private IFeignService feignService;

    /**
     * 会员结算报表
     * @author 伍将
     * @date 2020/11/13
     * @param startDate: 开始时间
     **/
    @Override
    public void memberSettlementReport(DateTime startDate) {
        logger.info("会员结算,结算日期：{}",startDate.toJdkDate());
        // 物流
        logisticsSettlement(startDate);
        // 生产通知单
        productionNoticeSettlement(startDate);
        //订单结算(包含账期与月结)
        orderSettlement(DateUtil.date());

    }

    /**
     * 手动结算
     * @author 伍将
     * @date 2020/12/24
     * @param settlementVO: 结算策略
     * @return : 统一数据模型
     **/
    @Transactional
    @Override
    public Wrapper<Void> manualSettlement(MemberManualSettlementVO settlementVO) {
        MemberSettlementDO settlementDO = memberSettlementRepository.findById(settlementVO.getId()).orElse(null);
        if (settlementDO == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }

        List<MemberSettlementDO> settlementDOList = memberSettlementRepository.findAllByIdGreaterThanEqualAndMemberIdAndRoleIdAndOrderTypeOrderBySettlementDateAsc(settlementVO.getId(), settlementDO.getMemberId(), settlementDO.getRoleId(), settlementDO.getOrderType());
        if (settlementDOList == null || settlementDOList.size() <= 0) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_NOT_EXIST);
        }

        // 物流结算
        if (SettlementOrderTypeEnum.LOGISTICS.getCode().equals(settlementDO.getOrderType())) {
            return logisticsManualSettlement(settlementDO, settlementDOList);
        }
        // 加工结算
        else if (SettlementOrderTypeEnum.PRODUCTION_NOTICE.getCode().equals(settlementDO.getOrderType())) {
            return productionNoticeManualSettlement(settlementDO, settlementDOList);
        }// 订单结算
        else if (SettlementOrderTypeEnum.ORDER.getCode().equals(settlementDO.getOrderType())) {
            return orderManualSettlement(settlementDO, settlementDOList);
        } else {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_ORDER_TYPE_ERROR);
        }
    }

    /**
     * 物流手动结算
     * @author 伍将
     * @date 2020/12/24
     * @param settlementDO: 当前结算数据
     * @param settlementDOList: 待重新结算数据集合
     * @return : 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> logisticsManualSettlement(MemberSettlementDO settlementDO, List<MemberSettlementDO> settlementDOList) {

        // 如果结算数据有已对账数据，则不允许重跑结算
        if (settlementDOList.stream().anyMatch(o -> !SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(o.getStatus()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }

        List<Long> idList = settlementDOList.stream().map(MemberSettlementDO::getId).collect(Collectors.toList());

        // 重新结算前，先清除旧数据
        settlementDOList.forEach(o -> {
            o.setTotalCount(0L);
            o.setAmount(0D);
        });
        memberSettlementRepository.saveAll(settlementDOList);
        memberLogisticsSettlementRepository.deleteAllBySettlementIdIn(idList);

        for (MemberSettlementDO o : settlementDOList) {
            MemberSettlementStrategyDO strategyDO = memberSettlementStrategyRepository.findById(settlementDO.getStrategyId()).orElse(null);
            // 如果对应的结算策略已删除或无效,则删除当前结算数据
            if (strategyDO == null || SettlementStrategyStatusEnum.INVALID.getCode().equals(strategyDO.getStatus())) {
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberLogisticsSettlementRepository.deleteAllBySettlementId(o.getId());
            } else if (strategyDO.getMemberList() != null && strategyDO.getMemberList().size() > 0
                    && strategyDO.getMemberList().stream().anyMatch(item ->
                    item.getMemberId().equals(o.getMemberId()) && item.getRoleId().equals(o.getRoleId()))) {

                // 如果策略设置指定会员，结算会员在策略中,直接重新结算
                logisticsSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), o.getMemberId(), o.getRoleId());
            } else {
                // 不在结算策略中,删除结算数据
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberLogisticsSettlementRepository.deleteAllBySettlementId(o.getId());
            }
        }

        return Wrapper.success();
    }

    /**
     * 加工手动结算
     * @author 伍将
     * @date 2020/12/24
     * @param settlementDO: 当前结算数据
     * @param settlementDOList: 待重新结算数据集合
     * @return : 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> productionNoticeManualSettlement(MemberSettlementDO settlementDO, List<MemberSettlementDO> settlementDOList) {

        // 如果结算数据有已对账数据，则不允许重跑结算
        if (settlementDOList.stream().anyMatch(o -> !SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(o.getStatus()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }

        List<Long> idList = settlementDOList.stream().map(MemberSettlementDO::getId).collect(Collectors.toList());

        // 重新结算前，先清除旧数据
        settlementDOList.forEach(o -> {
            o.setTotalCount(0L);
            o.setAmount(0D);
        });
        memberSettlementRepository.saveAll(settlementDOList);
        memberProductionNoticeSettlementRepository.deleteAllBySettlementIdIn(idList);

        for (MemberSettlementDO o : settlementDOList) {
            MemberSettlementStrategyDO strategyDO = memberSettlementStrategyRepository.findById(settlementDO.getStrategyId()).orElse(null);
            // 如果对应的结算策略已删除或无效,则删除当前结算数据
            if (strategyDO == null || SettlementStrategyStatusEnum.INVALID.getCode().equals(strategyDO.getStatus())) {
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberProductionNoticeSettlementRepository.deleteAllBySettlementId(o.getId());
            } else if (strategyDO.getMemberList() != null && strategyDO.getMemberList().size() > 0
                    && strategyDO.getMemberList().stream().anyMatch(item ->
                    item.getMemberId().equals(o.getMemberId()) && item.getRoleId().equals(o.getRoleId()))) {

                // 如果策略设置指定会员，结算会员在策略中,直接重新结算
                productionNoticeSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), o.getMemberId(), o.getRoleId());
            } else {
                // 不在结算策略中,删除结算数据
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberProductionNoticeSettlementRepository.deleteAllBySettlementId(o.getId());
            }
        }

        return Wrapper.success();
    }

    /**
     * 订单手动结算
     * @author ds
     * @date 2020/4/27
     * @param settlementDO: 当前结算数据
     * @param settlementDOList: 待重新结算数据集合
     * @return : 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> orderManualSettlement(MemberSettlementDO settlementDO, List<MemberSettlementDO> settlementDOList) {

        // 如果结算数据有已对账数据，则不允许重跑结算
        if (settlementDOList.stream().anyMatch(o -> !SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(o.getStatus()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }

        List<Long> idList = settlementDOList.stream().map(MemberSettlementDO::getId).collect(Collectors.toList());

        // 重新结算前，先清除旧数据
        settlementDOList.forEach(o -> {
            o.setTotalCount(0L);
            o.setAmount(0D);
        });
        memberSettlementRepository.saveAll(settlementDOList);
        memberOrderSettlementRepository.deleteAllBySettlementIdIn(idList);

        // 如果对应的结算策略已删除或无效,则删除当前结算数据
        settlementDOList.forEach(o -> {
            MemberSettlementStrategyDO strategyDO = memberSettlementStrategyRepository.findById(settlementDO.getStrategyId()).orElse(null);
            if (strategyDO == null || SettlementStrategyStatusEnum.INVALID.getCode().equals(strategyDO.getStatus())) {
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberOrderSettlementRepository.deleteAllBySettlementId(o.getId());
            } else if (strategyDO.getMemberList() != null && strategyDO.getMemberList().size() > 0
                    && strategyDO.getMemberList().stream().anyMatch(item ->
                    item.getMemberId().equals(o.getPayMemberId())&&item.getRoleId().equals(o.getPayRoleId()))) {

                // 如果策略设置指定会员，结算会员在策略中,直接重新结算
                orderSettlementCommon(strategyDO, DateUtil.date(o.getSettlementDate()), o.getPayMemberId(), o.getPayRoleId());
            } else {
                // 不在结算策略中,删除结算数据
                // 删除结算数据
                memberSettlementRepository.deleteById(o.getId());
                // 删除结算明细
                memberOrderSettlementRepository.deleteAllBySettlementId(o.getId());
            }
        });

        return Wrapper.success();
    }

    /**
     * 根据结算日进行生产通知单结算
     * @author 伍将
     * @date 2020/11/14
     * @param startDate: 结算日期
     **/
    private void productionNoticeSettlement(DateTime startDate) {

        logger.info("开始统计会员结算-生产通知单");

        Integer day = startDate.dayOfMonth();

        // 查询物流有效结算策略
        List<MemberSettlementStrategyDO> strategyDOS = getMemberSettlementStrategyList(day, SettlementOrderTypeEnum.PRODUCTION_NOTICE.getCode());
        if (strategyDOS.size() <= 0) {
            logger.info("统计会员结算-生产通知单:当前没有符合需要结算的策略");
        } else {
            // 循环处理各策略数据
            for (MemberSettlementStrategyDO strategyDO : strategyDOS) {
                logger.info("统计会员结算-生产通知单:start:{}-{}", strategyDO.getId(), strategyDO.getName());
                productionNoticeSettlementForStrategy(strategyDO, startDate);
                logger.info("统计会员结算-生产通知单:end:{}-{}", strategyDO.getId(), strategyDO.getName());
            }
        }

        logger.info("结束统计会员结算-生产通知单");
    }

    /**
     * 根据策略进行生成通知单结算
     * @author 伍将
     * @date 2020/11/14
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     **/
    @Transactional
    public void productionNoticeSettlementForStrategy(MemberSettlementStrategyDO strategyDO, DateTime startDate) {
        if (strategyDO == null) {
            logger.info("统计会员结算-生产通知单:结算策略为空");
            return;
        }
        if (strategyDO.getMemberList() == null || strategyDO.getMemberList().size() <= 0) {
            logger.info("统计会员结算-生产通知单:结算策略[{}-{}]未有下属会员", strategyDO.getId(), strategyDO.getName());
            return;
        }
        //生产通知单由付款方创建
        Long payMemberId = strategyDO.getMemberId();
        Long payRoleId = strategyDO.getRoleId();
        for (MemberSettlementStrategyMemberDO memberDO : strategyDO.getMemberList()) {
            //[生产通知单]策略下的会员为收款方
            Long memberId = memberDO.getMemberId();
            Long roleId = memberDO.getRoleId();
            try {
                Wrapper<Void> wrapper = productionNoticeSettlementCommon(strategyDO, startDate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计会员结算-生产通知单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
                }
            } catch (Exception e) {
                logger.info("统计会员结算-生产通知单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
            }
        }
    }

    /**
     * 加工结算通用方法
     * @author 伍将
     * @date 2020/12/24
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param memberId: 结算会员id
     * @param roleId: 结算会员角色id
     * @return : 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> productionNoticeSettlementCommon(MemberSettlementStrategyDO strategyDO, DateTime startDate, Long memberId, Long roleId) {

        if (strategyDO == null) {
            logger.info("统计会员结算-生产通知单:结算策略为空");
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_STRATEGY_NOT_EXIST);
        }

        Long payMemberId = strategyDO.getMemberId();
        Long payRoleId = strategyDO.getRoleId();

        Long settlementDate = DateUtil.parseDate(startDate.toString("yyyy-MM-dd")).getTime();

        // 查询是否存在结算数据
        MemberSettlementDO memberSettlementDO = memberSettlementRepository.getDetail(settlementDate, memberId, roleId, payMemberId, payRoleId, SettlementOrderTypeEnum.PRODUCTION_NOTICE.getCode(), strategyDO.getSettlementWay());
        if (memberSettlementDO != null) {
            if (!memberSettlementDO.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
                logger.info("统计会员结算-生产通知单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],已结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }

            // 清除原始数据
            memberSettlementDO.setTotalCount(0L);
            memberSettlementDO.setAmount(0D);
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);

            // 删除生成通知单结算明细
            memberProductionNoticeSettlementRepository.deleteAllBySettlementId(memberSettlementDO.getId());
        } else {

            memberSettlementDO = new MemberSettlementDO();
            memberSettlementDO.setMemberId(memberId);
            memberSettlementDO.setRoleId(roleId);
            memberSettlementDO.setSettlementName("-");
            memberSettlementDO.setPayMemberId(payMemberId);
            memberSettlementDO.setPayRoleId(payRoleId);
            memberSettlementDO.setPayName("-");

            // 获取会员名称
            List<Long> memberIds = new ArrayList<>();
            memberIds.add(memberId);
            memberIds.add(payMemberId);
            List<MemberBO> memberBOS = memberDAO.getMemberList(memberIds);
            if (memberBOS != null && memberBOS.size() > 0) {
                MemberBO memberBO = memberBOS.stream().filter(o -> o.getId().equals(memberId)).findFirst().orElse(null);
                memberSettlementDO.setSettlementName(memberBO != null ? memberBO.getName() : "-");
                memberBO = memberBOS.stream().filter(o -> o.getId().equals(payMemberId)).findFirst().orElse(null);
                memberSettlementDO.setPayName(memberBO != null ? memberBO.getName() : "-");
            }

            //交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            memberSettlementDO.setSettlementNo(settlementNo);
            memberSettlementDO.setSettlementDate(settlementDate);
            memberSettlementDO.setSettlementWay(strategyDO.getSettlementWay());
            memberSettlementDO.setOrderType(SettlementOrderTypeEnum.PRODUCTION_NOTICE.getCode());
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementDO.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            memberSettlementDO.setPayWay(strategyDO.getSettlementPaymentType());
            memberSettlementDO.setStrategyId(strategyDO.getId());
            memberSettlementDO.setUpdateTime(0L);
        }

        long prePayTime;
        long endTime;
        if (SettlementWayEnum.ACCOUNT_PERIOD.getCode().equals(strategyDO.getSettlementWay())) {
            // 账期结算截止时间:结算最后时间
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(startDate,-strategyDO.getSettlementDays())).getTime();
        } else if (SettlementWayEnum.MONTH_SETTLEMENT.getCode().equals(strategyDO.getSettlementWay())) {
            // 月结结算截止时间:上个月最后一天时间
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(startDate, -1)).getTime();
        } else {
            logger.info("统计会员结算-生成通知单:结算策略[{}-{}]结算方式有误", strategyDO.getId(), strategyDO.getName());
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }

        // 获取最后一次生成通知单结算
        MemberProductionNoticeSettlementDO memberProductionNoticeSettlementDO = memberProductionNoticeSettlementRepository.findFirstByMemberIdAndRoleIdAndPayMemberIdAndPayRoleIdOrderByReceiveTimeDesc(memberId, roleId, payMemberId, payRoleId);
        // 在原有时间上加1，防止后续查询数据重复
        Long startTime = (memberProductionNoticeSettlementDO != null ? (memberProductionNoticeSettlementDO.getReceiveTime() + 1) : 0);

        // 设置结算时间范围,预付款时间
        // 设置预计付款日期
        LocalDateTime settlementDatetime = Instant.ofEpochMilli(settlementDate).atZone(ZoneId.systemDefault()).toLocalDateTime();
        long estimatedPaymentDate = Optional.ofNullable(strategyDO.getEstimatedPaymentDate()).map(map -> {
            LocalDateTime estimatedPaymentDatetime = settlementDatetime.withDayOfMonth(map);
            return estimatedPaymentDatetime.compareTo(settlementDatetime) >= 0
                    ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                    : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        }).orElse(settlementDate);
        memberSettlementDO.setPrePayTime(estimatedPaymentDate);
        memberSettlementDO.setStartTime(startTime);
        memberSettlementDO.setEndTime(endTime);

        MemberSettlementBO memberSettlementBO = produceNoticeOrderDAO.getSettlement(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId);
        if (memberSettlementBO != null) {
            // 保存生成通知单结算
            memberSettlementDO.setTotalCount(memberSettlementBO.getTotalCount());
            memberSettlementDO.setAmount(memberSettlementBO.getTotalAmount());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);
            Long settlementId = memberSettlementDO.getId();

            // 处理生成通知单结算明细
            List<MemberProductionNoticeSettlementDO> detailList = produceNoticeOrderDAO.getSettlementDetail(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId);
            if (detailList != null && detailList.size() > 0) {
                for (MemberProductionNoticeSettlementDO o : detailList) {
                    o.setSettlementId(settlementId);
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setPayMemberId(payMemberId);
                    o.setPayRoleId(payRoleId);
                }
                memberProductionNoticeSettlementRepository.saveAll(detailList);
            }

            // 新增系统消息通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(memberSettlementDO.getMemberId());
            request.setRoleId(memberSettlementDO.getRoleId());
            request.setMessageNotice(MessageTemplateCode.receipt_settle_check);
            List<String> params = new ArrayList<>();
            params.add(memberSettlementDO.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        } else {

            // 删除会员结算数据
            if (memberSettlementDO.getId() != null && memberSettlementDO.getId() > 0) {
                memberSettlementRepository.delete(memberSettlementDO);
            }

            logger.info("统计会员结算-生产通知单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],无需结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
        }

        return Wrapper.success();
    }

    /**
     * 根据结算日进行物流单结算
     * @author 伍将
     * @date 2020/11/13
     * @param startDate: 结算时间
     **/
    private void logisticsSettlement(DateTime startDate) {

        logger.info("开始统计会员结算-物流单");

        // 获取当前时间对应的天
        Integer day = startDate.dayOfMonth();

        // 查询物流有效结算策略
        List<MemberSettlementStrategyDO> strategyDOS = getMemberSettlementStrategyList(day, SettlementOrderTypeEnum.LOGISTICS.getCode());
        if (strategyDOS.size() <= 0) {
            logger.info("统计会员结算-物流单:当前没有符合需要结算的策略");
        } else {
            // 循环处理各策略数据
            for (MemberSettlementStrategyDO strategyDO : strategyDOS) {
                logger.info("统计会员结算-物流单:start:{}-{}", strategyDO.getId(), strategyDO.getName());
                logisticsSettlementForStrategy(strategyDO, startDate);
                logger.info("统计会员结算-物流单:end:{}-{}", strategyDO.getId(), strategyDO.getName());
            }
        }

        logger.info("结束统计会员结算-物流单");
    }

    /**
     * 根据策略进行物流结算
     * @author 伍将
     * @date 2020/11/13
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     **/
    @Transactional
    public void logisticsSettlementForStrategy(MemberSettlementStrategyDO strategyDO, DateTime startDate) {
        if (strategyDO == null) {
            logger.info("统计会员结算-物流单:结算策略为空");
            return;
        }
        if (strategyDO.getMemberList() == null || strategyDO.getMemberList().size() <= 0) {
            logger.info("统计会员结算-物流单:结算策略[{}-{}]未有下属会员", strategyDO.getId(), strategyDO.getName());
            return;
        }
        //物流策略由付款方创建
        Long payMemberId = strategyDO.getMemberId();
        Long payRoleId = strategyDO.getRoleId();
        for (MemberSettlementStrategyMemberDO memberDO : strategyDO.getMemberList()) {
            //策略下的会员为收款方
            Long memberId = memberDO.getMemberId();
            Long roleId = memberDO.getRoleId();
            try {
                Wrapper<Void> wrapper = logisticsSettlementCommon(strategyDO, startDate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计会员结算-物流单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
                }
            } catch (Exception e) {
                logger.info("统计会员结算-物流单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
            }
        }
    }

    /**
     * 物流结算通用方法
     * @author 伍将
     * @date 2020/12/24
     * @param strategyDO: 结算策略
     * @param startDate: 结算时间
     * @param memberId: 结算会员id
     * @param roleId: 结算会员角色id
     * @return : 统一数据模型
     **/
    @Transactional
    public Wrapper<Void> logisticsSettlementCommon(MemberSettlementStrategyDO strategyDO, DateTime startDate, Long memberId, Long roleId) {
        if (strategyDO == null) {
            throw new BusinessException(ResponseCode.SA_MEMBER_SETTLEMENT_STRATEGY_NOT_EXIST);
        }

        Long payMemberId = strategyDO.getMemberId();
        Long payRoleId = strategyDO.getRoleId();
        Long settlementDate = DateUtil.parseDate(startDate.toString("yyyy-MM-dd")).getTime();

        // 查询是否存在结算数据
        MemberSettlementDO memberSettlementDO = memberSettlementRepository.getDetail(settlementDate, memberId, roleId, payMemberId, payRoleId, SettlementOrderTypeEnum.LOGISTICS.getCode(), strategyDO.getSettlementWay());
        if (memberSettlementDO != null) {
            if (!SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode().equals(memberSettlementDO.getStatus())) {
                logger.info("统计会员结算-物流单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],已结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }

            // 清除原始数据
            memberSettlementDO.setTotalCount(0L);
            memberSettlementDO.setAmount(0D);
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);

            // 删除物流结算明细
            memberLogisticsSettlementRepository.deleteAllBySettlementId(memberSettlementDO.getId());
        } else {

            memberSettlementDO = new MemberSettlementDO();
            memberSettlementDO.setMemberId(memberId);
            memberSettlementDO.setRoleId(roleId);
            memberSettlementDO.setSettlementName("-");
            memberSettlementDO.setPayMemberId(payMemberId);
            memberSettlementDO.setPayRoleId(payRoleId);
            memberSettlementDO.setPayName("-");

            // 获取会员名称
            List<Long> memberIds = new ArrayList<>();
            memberIds.add(memberId);
            memberIds.add(payMemberId);
            List<MemberBO> memberBOS = memberDAO.getMemberList(memberIds);
            if (memberBOS != null && memberBOS.size() > 0) {
                MemberBO memberBO = memberBOS.stream().filter(o -> o.getId().equals(memberId)).findFirst().orElse(null);
                memberSettlementDO.setSettlementName(memberBO != null ? memberBO.getName() : "-");
                memberBO = memberBOS.stream().filter(o -> o.getId().equals(payMemberId)).findFirst().orElse(null);
                memberSettlementDO.setPayName(memberBO != null ? memberBO.getName() : "-");
            }

            //交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            memberSettlementDO.setSettlementNo(settlementNo);
            memberSettlementDO.setSettlementDate(settlementDate);
            // 设置预付款日期

            memberSettlementDO.setSettlementWay(strategyDO.getSettlementWay());
            memberSettlementDO.setOrderType(SettlementOrderTypeEnum.LOGISTICS.getCode());
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementDO.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            memberSettlementDO.setPayWay(strategyDO.getSettlementPaymentType());
            memberSettlementDO.setStrategyId(strategyDO.getId());
            memberSettlementDO.setUpdateTime(0L);
        }

        long endTime;
        if (SettlementWayEnum.ACCOUNT_PERIOD.getCode().equals(strategyDO.getSettlementWay())) {
            // 账期结算截止时间:结算最后时间
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(startDate,-strategyDO.getSettlementDays())).getTime();
        } else if (SettlementWayEnum.MONTH_SETTLEMENT.getCode().equals(strategyDO.getSettlementWay())) {
            // 月结结算截止时间:上个月最后一天时间
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(startDate, -1)).getTime();
        } else {
            logger.info("统计会员结算-生成通知单:结算策略[{}-{}]结算方式有误", strategyDO.getId(), strategyDO.getName());
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }

        // 获取最后一次物流结算
        MemberLogisticsSettlementDO memberLogisticsSettlementDO = memberLogisticsSettlementRepository.findFirstByMemberIdAndRoleIdAndPayMemberIdAndPayRoleIdOrderByAcceptOrderTimeDesc(memberId, roleId, payMemberId, payRoleId);
        //在原有时间上加1，防止后续查询数据重复
        Long startTime = (memberLogisticsSettlementDO != null ? (memberLogisticsSettlementDO.getAcceptOrderTime() + 1) : 0);

        // 设置结算时间范围与预支付时间
        memberSettlementDO.setPrePayTime(settlementDate);
        memberSettlementDO.setStartTime(startTime);
        memberSettlementDO.setEndTime(endTime);

        LogisticsSettlementBO logisticsSettlementBO = logisticsOrderDAO.getSettlement(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId);
        if (logisticsSettlementBO != null) {
            // 保存物流结算
            memberSettlementDO.setTotalCount(logisticsSettlementBO.getTotalCount());
            memberSettlementDO.setAmount(logisticsSettlementBO.getTotalAmount());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);
            Long settlementId = memberSettlementDO.getId();

            // 处理物流结算明细
            List<MemberLogisticsSettlementDO> detailList = logisticsOrderDAO.getSettlementDetail(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId);
            if (detailList != null && detailList.size() > 0) {
                for (MemberLogisticsSettlementDO o : detailList) {
                    o.setSettlementId(settlementId);
                    o.setSettlementAmount(o.getOrderAmount());
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setPayMemberId(payMemberId);
                    o.setPayRoleId(payRoleId);
                }
                memberLogisticsSettlementRepository.saveAll(detailList);
            }

            // 新增系统消息通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(memberSettlementDO.getMemberId());
            request.setRoleId(memberSettlementDO.getRoleId());
            request.setMessageNotice(MessageTemplateCode.receipt_settle_check);
            List<String> params = new ArrayList<>();
            params.add(memberSettlementDO.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        } else {

            // 删除会员结算数据
            if (memberSettlementDO.getId() != null && memberSettlementDO.getId() > 0) {
                memberSettlementRepository.delete(memberSettlementDO);
            }
            logger.info("统计会员结算-物流单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],无需结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
        }

        return Wrapper.success();
    }

    /**
     * 获取有效结算策略
     * @author 伍将
     * @date 2020/11/13
     * @param day :结算日
     * @param settlementOrderType:结算单据类型
     * @return : java.util.List<com.ssy.lingxi.report.entity.settleaccount.MemberSettlementStrategyDO>
     **/
    private List<MemberSettlementStrategyDO> getMemberSettlementStrategyList(Integer day, Integer settlementOrderType) {
        DateTime now = DateTime.now();
        //得到今天是本月的第几天
        int today = now.dayOfMonth();
        //获得本月最后一天
        int monthMaxDay = DateUtil.endOfMonth(now).dayOfMonth();
        Specification<MemberSettlementStrategyDO> spec =  (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), SettlementStrategyStatusEnum.VALID.getCode())); // 查询有效
            list.add(criteriaBuilder.equal(root.get("settlementOrderType").as(Integer.class), settlementOrderType)); // 结算单据方式
            Predicate settlementWayPeriod = criteriaBuilder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.ACCOUNT_PERIOD.getCode());
            Predicate settlementWayMonth = criteriaBuilder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.MONTH_SETTLEMENT.getCode());
            Predicate settlementDate;
            if(today!=monthMaxDay||monthMaxDay==31){//如果不是月末最后一天
                settlementDate=criteriaBuilder.equal(root.get("settlementDate").as(Integer.class), today);
            }else{
                settlementDate=criteriaBuilder.greaterThanOrEqualTo(root.get("settlementDate").as(Integer.class), today);
            }
            list.add(criteriaBuilder.or(settlementWayPeriod,criteriaBuilder.and(settlementWayMonth,settlementDate)));
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return memberSettlementStrategyRepository.findAll(spec);
    }

    /**
     * 会员票据报表
     * @author 伍将
     * @date 2020/12/15
     **/
    @Override
    public void memberReceiptInvoiceReport() {
        logger.info("统计会员结算-票据报表:start");

        try {
            // 销售订单
            orderInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:销售订单统计失败:{}", e.getMessage());
        }
        try {
            // 售后退货
            returnGoodsInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:售后退货统计失败:{}", e.getMessage());
        }
        try {
            // 结算积分
            scoreInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:结算积分统计失败:{}", e.getMessage());
        }
        try {
            // 结算物流
            logisticsInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:结算物流统计失败:{}", e.getMessage());
        }
        try {
            // 结算加工
            productionNoticeInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:结算加工统计失败:{}", e.getMessage());
        }
        try {
            // 结算请款
            applyAmountOrderInvoiceReport();
        } catch (Exception e) {
            logger.info("统计会员结算-票据报表:结算请款统计失败:{}", e.getMessage());
        }
    }

    /**
     * 销售订单票据报表
     * @author 伍将
     * @date 2020/12/16
     **/
    private void orderInvoiceReport() {
        int index = 0;
        int count = 1000;
        while (true) {
            List<MemberReceiptInvoiceBO> resultList = procurementOrderDAO.pageInvoiceReport(index, count);
            // 没有查询到相关数据，跳出循环
            if (resultList == null || resultList.size() <= 0) {
                break;
            }

            // 更新票据报表数据
            memberReceiptInvoiceDAO.updateInvoiceReport(resultList);

            // 当查询的数据数量小于查询数时说明已查询完所有数据，则跳出循环
            if (resultList.size() < count) {
                break;
            } else {
                index += count;
            }
        }
    }

    /**
     * 退货票据报表
     * @author 伍将
     * @date 2020/12/16
     **/
    private void returnGoodsInvoiceReport() {
        int index = 0;
        int count = 1000;
        while (true) {
            List<MemberReceiptInvoiceBO> resultList = returnGoodsDAO.pageInvoiceReport(index, count);
            // 没有查询到相关数据，跳出循环
            if (resultList == null || resultList.size() <= 0) {
                break;
            }

            // 更新票据报表数据
            memberReceiptInvoiceDAO.updateInvoiceReport(resultList);

            // 当查询的数据数量小于查询数时说明已查询完所有数据，则跳出循环
            if (resultList.size() < count) {
                break;
            } else {
                index += count;
            }
        }
    }

    /**
     * 积分票据报表
     * @author 伍将
     * @date 2020/12/15
     **/
    private void scoreInvoiceReport() {
        memberReceiptInvoiceDAO.updateScoreInvoiceReport();
    }

    /**
     * 物流票据报表
     * @author 伍将
     * @date 2020/12/15
     **/
    private void logisticsInvoiceReport() {
        memberReceiptInvoiceDAO.updateLogisticsInvoiceReport();
    }

    /**
     * 加工票据报表
     * @author 伍将
     * @date 2020/12/15
     **/
    private void productionNoticeInvoiceReport() {
        memberReceiptInvoiceDAO.updateProductionNoticeInvoiceReport();
    }

    /**
     * 会员票据通知
     * @author 伍将
     * @date 2020/12/22
     **/
    @Override
    public void memberReceiptInvoiceNotice() {

        // 查询待开票数据
        List<StatisticsCountBO> boList = memberReceiptInvoiceDAO.getToBeInvoiceList();

        // 发送系统通知
        if (boList != null && boList.size() > 0) {
            List<SystemMessageRequest> requestList = boList.stream().map(r -> {
                SystemMessageRequest request = new SystemMessageRequest();
                request.setMemberId(r.getMemberId());
                request.setRoleId(r.getRoleId());
                request.setMessageNotice(MessageTemplateCode.receipt_settle_invoice);
                List<String> params = new ArrayList<>();
                params.add(r.getCount().toString());
                request.setParams(params);

                return request;
            }).collect(Collectors.toList());
            feignService.batchSendSystemMessage(requestList);
        }

    }

    /**
     * 根据结算日进行[订单]结算
     * @author ds
     * @date 2021/3/29
     **/
    private void orderSettlement(DateTime startDate) {

        logger.info("开始统计会员结算-订单");

        Integer day = startDate.dayOfMonth();

        // 查询订单有效结算策略
        List<MemberSettlementStrategyDO> strategyDOS = getMemberSettlementStrategyList(day, SettlementOrderTypeEnum.ORDER.getCode());
        if (strategyDOS.size() <= 0) {
            logger.info("统计会员结算-订单:当前没有符合需要结算的策略");
        } else {
            // 循环处理各策略数据
            for (MemberSettlementStrategyDO strategyDO : strategyDOS) {
                logger.info("统计会员结算-订单:start:{}-{}", strategyDO.getId(), strategyDO.getName());
                orderSettlementForStrategy(strategyDO, startDate);
                logger.info("统计会员结算-订单:end:{}-{}", strategyDO.getId(), strategyDO.getName());
            }
        }

        logger.info("结束统计会员结算-订单");
    }

    /**
     * 根据策略进行订单结算
     * @author ds
     * @date 2021/3/29
     **/
    @Transactional
    public void orderSettlementForStrategy(MemberSettlementStrategyDO strategyDO, DateTime startDate) {
        if (strategyDO == null) {
            logger.info("统计会员结算-订单:结算策略为空");
            return;
        }
        if (strategyDO.getMemberList() == null || strategyDO.getMemberList().size() <= 0) {
            logger.info("统计会员结算-订单:结算策略[{}-{}]未有上级会员", strategyDO.getId(), strategyDO.getName());
            return;
        }
        //供应商为[订单]策略创建者（即收款方）
        Long receiveMemberId = strategyDO.getMemberId();
        Long receiveRoleId = strategyDO.getRoleId();
        for (MemberSettlementStrategyMemberDO memberDO : strategyDO.getMemberList()) {
            //付款方(采购商)
            Long memberId = memberDO.getMemberId();
            Long roleId = memberDO.getRoleId();
            try {
                Wrapper<Void> wrapper = orderSettlementCommon(strategyDO, startDate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计会员结算-订单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算失败", strategyDO.getId(), strategyDO.getName(),  receiveMemberId, receiveRoleId, memberId, roleId);
                }
            } catch (Exception e) {
                logger.info("统计会员结算-订单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],结算异常", strategyDO.getId(), strategyDO.getName(), receiveMemberId, receiveRoleId, memberId, roleId);
                logger.error("统计会员结算-订单:异常：",e);
            }
        }
    }


    /**
     * 订单结算通用方法（订单与退款申请单）
     * @param strategyDO 会员结算策略
     * @param startDate 开始时间
     * @param payMemberId 订单付款方：会员id
     * @param payRoleId 订单付款方：角色id
     * @return 订单结算通用结果
     */
    @Transactional
    public Wrapper<Void> orderSettlementCommon(MemberSettlementStrategyDO strategyDO, DateTime startDate, Long payMemberId, Long payRoleId) {
        //订单收款方(供应商)
        Long memberId = strategyDO.getMemberId();
        Long roleId = strategyDO.getRoleId();
        Long settlementDate = DateUtil.parseDate(startDate.toString("yyyy-MM-dd")).getTime();

        MemberSettlementDO memberSettlementDO = memberSettlementRepository.getDetail(settlementDate, memberId, roleId, payMemberId, payRoleId, SettlementOrderTypeEnum.ORDER.getCode(), strategyDO.getSettlementWay());
        if (memberSettlementDO != null) {
            if (!memberSettlementDO.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
                logger.info("统计会员结算-订单:结算策略[{}-{}-结算会员:{}-结算角色:{}-支付会员:{}-支付角色:{}],已结算", strategyDO.getId(), strategyDO.getName(), memberId, roleId, payMemberId, payRoleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }

            // 清除原始数据
            memberSettlementDO.setTotalCount(0L);
            memberSettlementDO.setAmount(0D);
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);

            // 删除订单结算明细
            memberOrderSettlementRepository.deleteAllBySettlementId(memberSettlementDO.getId());
        } else {

            memberSettlementDO = new MemberSettlementDO();
            memberSettlementDO.setMemberId(memberId);
            memberSettlementDO.setRoleId(roleId);
            memberSettlementDO.setSettlementName("-");
            memberSettlementDO.setPayMemberId(payMemberId);
            memberSettlementDO.setPayRoleId(payRoleId);
            memberSettlementDO.setPayName("-");
            memberSettlementDO.setTotalCount(0L);
            memberSettlementDO.setAmount(0D);
            // 获取会员名称
            List<Long> memberIds = new ArrayList<>();
            memberIds.add(memberId);
            memberIds.add(payMemberId);
            List<MemberBO> memberBOS = memberDAO.getMemberList(memberIds);
            if (memberBOS != null && memberBOS.size() > 0) {
                MemberBO memberBO = memberBOS.stream().filter(o -> o.getId().equals(memberId)).findFirst().orElse(null);
                memberSettlementDO.setSettlementName(memberBO != null ? memberBO.getName() : "-");
                memberBO = memberBOS.stream().filter(o -> o.getId().equals(payMemberId)).findFirst().orElse(null);
                memberSettlementDO.setPayName(memberBO != null ? memberBO.getName() : "-");
            }

            //交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            memberSettlementDO.setSettlementNo(settlementNo);
            memberSettlementDO.setSettlementDate(settlementDate);
            memberSettlementDO.setSettlementWay(strategyDO.getSettlementWay());
            memberSettlementDO.setOrderType(SettlementOrderTypeEnum.ORDER.getCode());
            memberSettlementDO.setSettlementTime(System.currentTimeMillis());
            memberSettlementDO.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            memberSettlementDO.setPayWay(strategyDO.getSettlementPaymentType());
            memberSettlementDO.setStrategyId(strategyDO.getId());
            memberSettlementDO.setUpdateTime(0L);
        }

        // 查询是否存在结算数据
        Integer settlementWay = strategyDO.getSettlementWay();
        long endTime;
        Integer payType;
        if (SettlementWayEnum.ACCOUNT_PERIOD.getCode().equals(settlementWay)) {
            // 账期结算截止时间:结算最后时间
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(startDate,-strategyDO.getSettlementDays())).getTime();
            payType= OrderPayChannelEnum.ACCOUNT_PERIOD.getCode();
        } else if (SettlementWayEnum.MONTH_SETTLEMENT.getCode().equals(settlementWay)) {
            // 月结结算截止时间:上个月最后一天时间
            endTime = DateUtil.endOfMonth(DateUtil.offsetMonth(startDate, -1)).getTime();
            payType=OrderPayChannelEnum.MONTHLY_SETTLEMENT.getCode();
        } else {
            logger.info("统计会员结算-订单:结算策略[{}-{}]结算方式有误", strategyDO.getId(), strategyDO.getName());
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }

        // 获取最后一次订单结算
        MemberOrderSettlementDO memberProductionNoticeSettlementDO = memberOrderSettlementRepository.findFirstByMemberIdAndRoleIdAndPayMemberIdAndPayRoleIdOrderByPayTimeDesc(memberId, roleId, payMemberId, payRoleId);
        //在原有时间上加1，防止后续查询数据重复
        Long startTime = (memberProductionNoticeSettlementDO != null ? (memberProductionNoticeSettlementDO.getPayTime() + 1000) : 0);
        // 设置预计付款日期
        LocalDateTime settlementDatetime = Instant.ofEpochMilli(settlementDate).atZone(ZoneId.systemDefault()).toLocalDateTime();
        long prePayTime = Optional.ofNullable(strategyDO.getEstimatedPaymentDate()).map(map -> {
            LocalDateTime estimatedPaymentDatetime = settlementDatetime.withDayOfMonth(map);
            return estimatedPaymentDatetime.compareTo(settlementDatetime) >= 0
                    ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                    : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        }).orElse(settlementDate);
        // 设置结算时间范围,预付款时间
        memberSettlementDO.setPrePayTime(prePayTime);
        memberSettlementDO.setStartTime(startTime);
        memberSettlementDO.setEndTime(endTime);
        // 处理订单数据
        MemberSettlementBO memberSettlementBO = procurementOrderDAO.getOrderSettlementByPurchase(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId,payType);
        if (memberSettlementBO != null) {
            // 保存订单结算
            memberSettlementDO.setTotalCount(memberSettlementBO.getTotalCount());
            memberSettlementDO.setAmount(memberSettlementBO.getTotalAmount());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);
            Long settlementId = memberSettlementDO.getId();

            // 处理订单结算明细
            List<MemberOrderSettlementCVO> detailList = procurementOrderDAO.getOrderSettlementDetailByPurchase(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId,payType);
            if (detailList != null && detailList.size() > 0) {
                List<MemberOrderSettlementDO> detailDOList=new ArrayList<>();
                for (MemberOrderSettlementCVO cvo : detailList) {
                    MemberOrderSettlementDO o=new MemberOrderSettlementDO();
                    BeanUtils.copyProperties(cvo,o);
                    o.setSettlementId(settlementId);
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setPayMemberId(payMemberId);
                    o.setPayRoleId(payRoleId);
                    o.setOrderType(SettlementMemberOrderTypeEnum.ORDER.getCode());
                    o.setSettlementAmount(o.getPayAmount());
                    o.setOrderAbstract(null != o.getOrderAbstract() ? o.getOrderAbstract() : "-");
                    detailDOList.add(o);
                }
                memberOrderSettlementRepository.saveAll(detailDOList);
            }
        }

        // 处理售后退款数据
        memberSettlementBO = returnGoodsDAO.getSettlementByPurchase(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId,payType);
        if (memberSettlementBO != null) {
            // 保存订单结算
            memberSettlementDO.setTotalCount(memberSettlementDO.getTotalCount() + memberSettlementBO.getTotalCount());
            memberSettlementDO.setAmount(memberSettlementDO.getAmount() - memberSettlementBO.getTotalAmount());
            memberSettlementRepository.saveAndFlush(memberSettlementDO);
            Long settlementId = memberSettlementDO.getId();

            // 处理订单结算明细
            List<MemberOrderSettlementDO> detailList = returnGoodsDAO.getSettlementDetailByPurchase(memberSettlementDO.getStartTime(), memberSettlementDO.getEndTime(), payMemberId, payRoleId, memberId, roleId,payType);
            if (detailList != null && detailList.size() > 0) {
                for (MemberOrderSettlementDO o : detailList) {
                    o.setSettlementId(settlementId);
                    o.setOrderType(SettlementMemberOrderTypeEnum.RETURN_GOODS.getCode());    // 退货
                    o.setMemberId(memberId);
                    o.setRoleId(roleId);
                    o.setPayMemberId(payMemberId);
                    o.setPayRoleId(payRoleId);
                    o.setPayAmount(-o.getPayAmount());
                    o.setSettlementAmount(-o.getPayAmount());
                    o.setOrderAbstract(null != o.getOrderAbstract() ? o.getOrderAbstract() : "-");
                    o.setType(o.getType());
                }
                memberOrderSettlementRepository.saveAll(detailList);
            }
        }

        if(memberSettlementDO.getId() != null&& memberSettlementDO.getTotalCount() >0){
            // 新增系统消息通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(memberSettlementDO.getMemberId());
            request.setRoleId(memberSettlementDO.getRoleId());
            request.setMessageNotice(MessageTemplateCode.receipt_settle_check);
            List<String> params = new ArrayList<>();
            params.add(memberSettlementDO.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        }

        // 如果新增了结算数据表，且结算订单小于等于0，则说明此次没有数据需要结算，删除平台结算数据
        if (memberSettlementDO.getId() != null && memberSettlementDO.getId() > 0
                && memberSettlementDO.getTotalCount() <= 0) {
            memberSettlementRepository.delete(memberSettlementDO);
        }

        return Wrapper.success();
    }

    /**
     * 请款单票据报表
     * @author ds
     * @date 2020/4/2
     **/
    private void applyAmountOrderInvoiceReport() {
        memberReceiptInvoiceDAO.updateApplyAmountOrderInvoiceReport();
    }

}
